package com.lauor.smpedr.core;

import com.lauor.smpedr.core.anno.AutoIncrement;
import com.lauor.smpedr.core.anno.Id;
import com.lauor.smpedr.core.anno.InsertIgnore;
import com.lauor.smpedr.core.anno.UpdateIgnore;
import com.lauor.smpedr.core.helper.EdrHelper;
import com.lauor.smpedr.core.helper.OrmHelper;
import com.lauor.smpedr.param.OptEnum;
import com.lauor.smpedr.param.SqlArgMap;
import com.lauor.smpedr.param.SqlParam;
import com.lauor.smpedr.utils.Str;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * mysql sql生成器
 */
public class SqlGeneratorMysql implements SqlGenerator {
    private final Logger LOG = LoggerFactory.getLogger(SqlGeneratorMysql.class);

    @Override
    public String generateQuery(String tableName, List<String> columns, SqlArgMap sqlArgs, SqlParam sqlParam) {
        this.checkTableName(tableName);

        String columnStr = "*";
        if (columns != null && !columns.isEmpty()){
            StringJoiner columnJoiner = new StringJoiner(",");
            for (String column : columns) {
                columnJoiner.add(column);
            }
            columnStr = columnJoiner.toString();
        }

        return new StringBuilder("select ")
                .append(columnStr)
                .append(" from ")
                .append(tableName)
                .append( this.generateWhereSuffix(sqlArgs, sqlParam) )
                .append(";").toString();
    }

    public String generateQuery(String tableName, SqlArgMap sqlArgs, SqlParam sqlParam) {
        return generateQuery(tableName, null, sqlArgs, sqlParam);
    }

    @Override
    public String generateCount(String tableName, SqlArgMap sqlArgs) {
        this.checkTableName(tableName);

        return new StringBuilder("select count(1) from ")
                .append(tableName)
                .append( this.generateWhereSuffix(sqlArgs, null) )
                .append(";").toString();
    }

    @Override
    public String generateWhereSuffix(SqlArgMap sqlArgs, SqlParam sqlParam) {
        if (sqlArgs == null || sqlArgs.isEmpty()) {
            return this.contactSqlSuffix("", sqlParam);
        }

        StringBuilder sqlWhereBuilder = new StringBuilder(" where ");
        int times = 0;
        for (Object key : sqlArgs.keySet()) {
            String keyStr = String.valueOf(key);
            SqlArgMap.ArgNode argNode = sqlArgs.getValue(keyStr);

            //集合类型
            if (OptEnum.EQ.isParamCollection( argNode.getOpt() ) && (argNode.getValue() instanceof Collection)){
                Collection paramList = (Collection) argNode.getValue();
                if (paramList == null || paramList.isEmpty()){
                    continue;
                }
                StringJoiner joiner = new StringJoiner(" , ", "(", ")");
                Iterator iterator = paramList.iterator();
                String sqlPlaceHolder = EdrHelper.generateSqlPlaceHolder(keyStr);
                while (iterator.hasNext()){
                    joiner.add(sqlPlaceHolder);
                    iterator.next();
                }
                if (times++ != 0){
                    sqlWhereBuilder.append( argNode.getRelateOpt().getBlankName() );
                }
                sqlWhereBuilder.append(keyStr)
                        .append( argNode.getOpt().getBlankName() )
                        .append( joiner.toString() );
            } else {
                if (times++ != 0){
                    sqlWhereBuilder.append( argNode.getRelateOpt().getBlankName() );
                }
                sqlWhereBuilder.append(keyStr)
                        .append( argNode.getOpt().getBlankName() )
                        .append( EdrHelper.generateSqlPlaceHolder(keyStr) );
            }
        }
        return this.contactSqlSuffix(sqlWhereBuilder.toString(), sqlParam);
    }

    public String generateBatchInsert(Class clazz, String tableName, List dataList, boolean insertNull) {
        this.checkTableName(tableName);
        if (dataList == null || dataList.isEmpty()) return "";

        StringBuilder sqlPrefixBuild = new StringBuilder("insert into ")
                .append(tableName)
                .append("(");
        StringJoiner sqlFieldJoiner = new StringJoiner(",", sqlPrefixBuild.toString(), ")");
        Field[] fields = clazz.getDeclaredFields();

        //TODO 找出插入忽略的字段
        Set<String> insertIgnoreFieldsName = EdrHelper.findFields(clazz, field -> field.isAnnotationPresent(InsertIgnore.class));
        //需要过滤的且值为空的字段
        Set<String> ignoreNullFieldNames;
        if (insertNull){//找到自增列进行空值判断
            ignoreNullFieldNames = EdrHelper.findFields(clazz, field -> {
                if ( field.isAnnotationPresent(AutoIncrement.class) ){
                    return true;
                }
                Id id = field.getAnnotation(Id.class);
                return id != null && id.autoIncrement();
            });
        } else {//所有列都需进行空值判断
            ignoreNullFieldNames = Stream.of(fields).map(f -> f.getName()).collect( Collectors.toSet() );
        }

        //TODO 生成sql field
        List<String> fieldValList = new LinkedList<>();
        try {
            for (Field field : fields) {
                String fieldName = field.getName();
                if ( insertIgnoreFieldsName.contains(fieldName) ) continue;

                Method ormMethod = OrmHelper.getGetterMethodByField(fieldName, clazz);
                if (ormMethod == null) continue;
                //过滤掉属性为空且需要过滤的字段
                if (ignoreNullFieldNames.contains(fieldName) && fieldNull(dataList, ormMethod) ){
                    continue;
                }

                sqlFieldJoiner.add( EdrHelper.getDbFieldName(field) );
                fieldValList.add( EdrHelper.generateSqlPlaceHolder(fieldName) );
            }
        } catch (Exception ex){
            LOG.error("generate batch insert sql error", ex);
            throw new RuntimeException(ex);
        }
        //TODO 构建values
        StringJoiner sqlValJoiner = new StringJoiner(",", " values(", ");");
        for (String field : fieldValList) {
            sqlValJoiner.add(field);
        }
        return sqlFieldJoiner.toString() + sqlValJoiner.toString();
    }
    /** 所有对象的该get方法获取的值都为null */
    private boolean fieldNull(List dataList, Method ormMethod) throws Exception {
        for (Object obj : dataList) {
            if (ormMethod.invoke(obj) != null) {
                return false;
            }
        }
        return true;
    }

    public String generateUpdate(String tableName, Object obj, SqlArgMap sqlArgs, SqlParam sqlParam, boolean updateNull) {
        this.checkTableName(tableName);

        StringBuilder sqlBuilder = new StringBuilder("update ");
        sqlBuilder.append(tableName)
                .append(" set ");
        Class cls = obj.getClass();

        Set<String> updateIgnoreFieldNames = EdrHelper.findFields(cls, field -> {
            if ( field.isAnnotationPresent(UpdateIgnore.class) ) return true;

            Id id = field.getAnnotation(Id.class);
            return id != null && id.updateIgnore();
        });
        Field[] fieldArr = cls.getDeclaredFields();
        StringJoiner sqlJoiner = new StringJoiner(",", sqlBuilder.toString(), "");

        for (Field field : fieldArr) {
            String fieldName = field.getName();
            if ( updateIgnoreFieldNames.contains(fieldName) ) continue;

            Method ormMethod = OrmHelper.getGetterMethodByField(fieldName, cls);
            if (ormMethod == null) continue;

            try {
                if (!updateNull && ormMethod.invoke(obj) == null) continue;

                String dbFieldName = EdrHelper.getDbFieldName(field);
                sqlJoiner.add(dbFieldName + " = " + EdrHelper.generateSqlPlaceHolder( field.getName() ));
            } catch (Exception ex){
                LOG.error("generate update sql error", ex);
                throw new RuntimeException(ex);
            }
        }
        //无where
        if (sqlArgs == null || sqlArgs.isEmpty()) return sqlJoiner.toString() + ";";

        return new StringBuilder( sqlJoiner.toString() )
                .append( this.generateWhereSuffix(sqlArgs, sqlParam) )
                .append(";").toString();
    }

    public String generateDelete(String tableName, SqlArgMap sqlArgs, SqlParam sqlParam) {
        this.checkTableName(tableName);

        return new StringBuilder("delete from ")
                .append(tableName)
                .append( this.generateWhereSuffix(sqlArgs, sqlParam) )
                .append(";").toString();
    }

    protected void checkTableName(String tableName){
        if ( Str.isEmpty(tableName) ) throw new IllegalArgumentException("tableName cannot be empty");
    }

    protected String contactSqlSuffix(String sql, SqlParam param){
        if (param == null || param.allNull(param)){
            return sql;
        }
        StringBuilder sqlBuilder = new StringBuilder(sql);
        if ( !Str.isEmpty( param.getGroupBy() ) ){
            sqlBuilder.append(" group by ")
                    .append( param.getGroupBy() );
        }
        if ( !Str.isEmpty( param.getHaving() ) ){
            sqlBuilder.append(" having ")
                    .append( param.getHaving() );
        }
        if ( !Str.isEmpty( param.getOrderBy() ) ){
            sqlBuilder.append(" order by ")
                    .append( param.getOrderBy() );
        }
        if ( param.hasLimit() ){
            if ( param.getLimit() > 0 ){
                sqlBuilder.append(" limit ")
                        .append( param.getLimit() );
            } else {
                sqlBuilder.append(" limit ")
                        .append(param.getStart())
                        .append(",")
                        .append( param.getPageSize() ).toString();
            }
        }
        return sqlBuilder.toString();
    }
}